/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package network;

import java.io.*;
import java.net.*;
import java.util.*;
import network.messages.*;

/**
 * Representa um cliente de socket
 * @author Victor
 */
public class Cliente implements Runnable
{
	private String _ipServidor;
	private int _portaServidor;
	private int _id;
	private Socket socket;
	private Formatter formatter;
	private Scanner scanner;
	private boolean _isConnected = false;
	private Thread threadListener;

	private Vector<NetworkListener> listeners = new Vector<NetworkListener>();

	/**
	 * Adiciona um listener
	 * @owner Victor
	 */
	public void AddListener(NetworkListener listener)
	{
		listeners.add(listener);
	}

	/**
	 * Remove um listener
	 * @owner Victor
	 */
	public void RemoveListener(NetworkListener listener)
	{
		listeners.remove(listener);
	}

	/**
	 * Retorna o ip em que esse cliente está conectado
	 * @owner Victor
	 */
	public String GetIpServidor()
	{
		return _ipServidor;
	}

	/**
	 * Retorna a porta do servidor em que esse cliente está conectado
	 * @owner Victor
	 */
	public int GetPortaServidor()
	{
		return _portaServidor;
	}

	/**
	 * Retorna o id do cliente
	 * @owner Victor
	 */
	public int GetId()
	{
		return _id;
	}

	/**
	 * Testa se o cliente está conectado
	 * @owner Victor
	 */
	public boolean IsConnected()
	{
		return _isConnected;
	}

	public Cliente()
	{

	}

		/**
	 * Escreve uma mensagem no log do servidor
	 * @owner Victor
	 */
	private void LogStringMessage(String mensagem)
	{
		for(NetworkListener listener : listeners)
		{
			listener.OnMensagemLog(mensagem);
		}
	}

	/**
	 * Escreve uma mensagem no log do servidor
	 * @owner Victor
	 */
	private void LogMessage(Mensagem mensagem)
	{
		for(NetworkListener listener : listeners)
		{
			listener.OnMensagemChegou(mensagem);
		}
	}

	/**
	 * Interpreta a mensagem recebida do servidor
	 * @owner Victor
	 */
	private void ParseMessage(String message)
	{
		Mensagem mensagem = MensagemFactory.GetMensagem(message);

		if (mensagem instanceof MensagemAssignId)
		{
			MensagemAssignId assignId = (MensagemAssignId)mensagem;
			_id = assignId.GetId();
		}

		LogMessage(mensagem);
	}

	/**
	 * Conecta o cliente no servidor
	 * @owner Victor
	 */
	public boolean Connect(String ip, int porta)
	{
		boolean ocorreuErro = false;
		try
		{
			socket = new Socket(ip, porta);
		}
		catch(UnknownHostException ex1)
		{
			ocorreuErro = true;
			ex1.printStackTrace();
		}
		catch(IOException ex2)
		{
			ocorreuErro = true;
			ex2.printStackTrace();
		}

		if (!ocorreuErro)
		{
			_isConnected = true;
			_ipServidor = ip;
			_portaServidor = porta;

			try
			{
				PrepareStreams(socket.getInputStream(), socket.getOutputStream());
			}
			catch(IOException ex1)
			{
				ex1.printStackTrace();
			}

			threadListener = new Thread(this);
			threadListener.start();
		}

		return ocorreuErro;
	}

	/**
	 * Disconecta o cliente do servidor
	 * @author Victor
	 */
	public void Disconnect()
	{
		try
		{
			socket.close();
			formatter.close();
			scanner.close();
			_isConnected = false;
			threadListener.interrupt();
		}
		catch(IOException ex1)
		{
			ex1.printStackTrace();
		}
	}

	/**
	 * Prepara os streams de leitura e escrita (Implementar)
	 * @author Victor
	 */
	protected void PrepareStreams(InputStream inputStream, OutputStream outputStream)
	{
		formatter = new Formatter(outputStream);
		scanner = new Scanner(inputStream);
	}

	/**
	 * Fica ouvindo o stream de input (executado em thread separada)
	 * @owner Victor
	 */
	protected void Listen()
	{
		try
		{
			this.ParseMessage(scanner.nextLine());
		}
		catch(Exception err)
		{
			
		}
	}

	/**
	 * Envia uma mensagem para todos os clientes (exceto você)
	 * @owner Victor
	 */
	public void SendContent(String content)
	{
		formatter.format("%s\n", content);
		formatter.flush();
	}
	
	/**
	 * Método implementado (pertence a interface Runnable)
	 * @owner Victor
	 */
	public void run()
	{
		while(IsConnected())
		{
			Listen();
		}
	}
}